ArchR takes as input aligned BAM or fragment files. These files are stored in the HDF5 file format (hierarchical data format version5). The HDF5 files are the constituent pieces of an ArchR analysis. They are called Arrow files. All Arrow files are grouped into a project, a compressed R data file. The Files are accessed in minimal chunks (parallel read and write operations). Therefore, in memory we do not have any large file sizes.

To select high quality cells TSS enrichment scores are used.

First steps in ArchR

Load libraries

library(ArchR)
library(knitr)
library(rhdf5)
library(uwot)
library(tidyverse)
#library(caret)
h5disableFileLocking()
inputFiles <- getTutorialData("Hematopoiesis")
addArchRGenome("hg19")

Create Arrow Files

  1. read accessible fragments
  2. calculate QC information for each cell (TSS enrichment scores and nucelosome info)
  3. filter cells based on QC parameters
  4. create genome-wide tile matrix using 500-bp bins
  5. create GeneScoreMatrix using gene annotation
ArrowFiles <- createArrowFiles(
  inputFiles = inputFiles,
  sampleNames = names(inputFiles),
  minTSS = 4, #Dont set this too high because you can always increase later
  minFrags = 1000, # minimum number of mapped fragments required
  # count matrix, instead of using peak it uses fixed-width sliding window of bins across the whole genome
  addTileMat = TRUE, 
  addGeneScoreMat = TRUE, # uses signal proximal to the TSS to estimate gene activity
  subThreading = FALSE,
  maxFrags = 1e+05,
  minFragSize = 10,
  maxFragSize = 2000,
  QCDir = "QualityControl",
  # the length in bp that wraps around nucleosomes -> 
    #identify fragments as sub-nucleosome spanning, mono-nucleosome spanning or multi-nucleosome spanning
  nucLength = 147, 
  # integer vector -> define region up/downstream of TSS to include as promoter region
  # can be used to calculate e.g fraction of reads in promoter (FIP)
  promoterRegion = c(2000, 100),
  # parameters for computing TSS enrichment scores, window (bp) centered at TSS = 101
  # flanking window = 2000 bp
  # norm = size of flank window used for normalization = 100 bp
  # accessibility within 101 bp surrounding the TSS will be normalized to accessibility
  # in 100 bp bins from -2000:-1901bp and 1901: 2000
  TSSParams = list(101, 2000, 100),
  # which chromosomes to exclude form downstream analysis
  # in human and mouse: mitochondrial DNA (chrM) and male sex chromosome (chrY)
  # the fragments are still stored in the arrow files
  excludeChr = c("chrM", "chrY"),
  # number of chunks to divide chromosomes in -> low-memory parallelized reading of input files
  nChunk = 5,
  # name of field in input bam file containing the barcode tag information
  bcTag = "qname",
  offsetPlus = 4, # offset applied to + stranded Tn5 insertion -> account for precise Tn5 binding site
  offsetMinus = -5, 
  logFile = createLogFile("createArrows")
)

Quality Control

Have a look at this for additional QC! https://bioconductor.org/packages/devel/bioc/vignettes/ATACseqQC/inst/doc/ATACseqQC.html

  1. the number of unique nuclear fragments (as opposed to mitochondrial fragments) A cell with very few usable fragments will not provide enough data to mak useful conclusions.
  2. signal-to-background ratio -> if this is low this probably corresponds to dying cells where the entire genome allows random transposition
  3. fragment size distribution -> since 147 bp are wrapped around a nucleosome it is expected that there are depletions of fragments of this length at regular intervals. We expect to see a periodic distribution of fragmetn size corresponding to nucleosomes (mono, di, tri, …), because Tn5 cannot cut DNA that is tightly wrapped around a nucleosome.

Inferring Doublets

Should be removed, because they can interfere with downstream analysis.

Doublet detection-and-removal algorithm: Heterotypic doublets are identified by generating a collection of synthetic doublets. These synthetic doublets are projected into low-dimensional embeddings. Searching for nearest neighbours to the synthetic doublets we can identify doublets in the dataset. This outperforms the prediction of doublets using fragment number (ROC-AUC). (Compared to demuxlet as ground truth)

We can also identify doublets in the scRNA-seq space if we have paired data and remove the cells in this way.

# for each sample provided doublet information will be assigned to each cell
# this way we can remove doublet-based clusters downstream
doubScores <- addDoubletScores(
  useMatrix = "TileMatrix",
  input = ArrowFiles,
  k = 10, #Refers to how many cells near a "pseudo-doublet" to count.
  nTrial = 5, # number of time to simulate nCell doublets 
  knnMethod = "UMAP", #Refers to the dimensionality reduciton method to use for nearest neighbor search.
  LSIMethod = 1 # oder of normalization: tf-log(idf)
)
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

# the doublet information is saved in a simpleListobject
# read in the object
doublet_summary <- readRDS("QualityControl/scATAC_BMMC_R1/scATAC_BMMC_R1-Doublet-Summary.rds")

doublet_summary[[2]] %>% head() %>% kable()
x y density type color
doublet_14733 5.8904443 -3.702020 0.0006007 simulated_doublet 0.0006007
doublet_14467 5.5716429 -3.579292 0.0006255 simulated_doublet 0.0006255
doublet_4584 5.4819946 -3.693586 0.0006264 simulated_doublet 0.0006264
doublet_14409 0.6149653 3.274105 0.0009510 simulated_doublet 0.0009510
doublet_12622 6.3805246 -3.755051 0.0010196 simulated_doublet 0.0010196
doublet_5702 5.2130489 -3.519199 0.0010345 simulated_doublet 0.0010345

# get first entry of the list = originalDataUMAP
p1 <- doublet_summary [[1]] %>% 
  ggplot() +
  geom_point(aes(x = X1, y = X2, col = enrichment), size = .1) +
  scale_color_viridis_c() +
  guides(fill=guide_legend(title="DoubletEnrichment")) +
  labs(title = "Simulated Doublet Enrichment over expectation")

p2 <- doublet_summary [[1]] %>% 
  ggplot() +
  geom_point(aes(x = X1, y = X2, col = score), size = .1) +
  scale_color_viridis_c() +
  guides(fill=guide_legend(title="DoubletScores -log10(P-adj)")) +
  labs(title = "Doublet Scores -log10(P-adj)")


p3 <- doublet_summary[[2]] %>% 
  ggplot() +
  geom_point(aes(x = x, y = y, col = density), size = .1) +
  scale_color_viridis_c() +
  guides(fill=guide_legend(title="Simulated Doublet Density")) +
  labs(title = "Doublet density")


p4 <- doublet_summary[[2]] %>% 
  ggplot() +
  geom_point(aes(x = x, y = y, col = density), size = .1) +
  geom_point(data = doublet_summary[[1]], aes(x = X1, y = X2), size = .1, alpha = .4) +
  scale_color_viridis_c() +
  guides(fill=guide_legend(title="Simulated Doublet Density")) +
  labs(title = "Simulated doublet density overlayed")

gridExtra::grid.arrange(p1, p2, p3, p4, ncol = 2)

Create ArchRProject

An ArchR Project is initialized with some important attributes:

  • ouput directory
  • sample names
  • sampleColData -> matrix containint data for each sample
  • cellColData -> contains data associated with each cell
    • after using addDoubletScore() there will be a column for “Doublet Enrichment” and “Doublet Score”
  • total number of cells (excluding doublets)
  • median TSS score & median number of fragments across all cells and samples
proj <- ArchRProject(
  ArrowFiles = ArrowFiles, 
  outputDirectory = "ArchRVignette",
  copyArrows = TRUE, #This is recommened so that you maintain an unaltered copy for later usage.
  geneAnnotation = getGeneAnnotation(),
  #genomeAnnotation = getGeneAnnotation(),
  showLogo = FALSE
)

Plot QC metrics

Data before QC and corresponding plots are saved in the Quality Control output folder.

log10(unique fragments) vs TSS enrichment

  • TSS enrichment score = signal-to-background
  • number of unique fragments -> cells with very few fragments do not have enough data to confidently analyze them
  • in the plot areas with more points/cells are colored in orange, and areas with less points in blue, indicating the distribution of cell
#create 3 separate dataframes for all samples
three_samples <- map(unique(proj$Sample), function(name){
  index <- BiocGenerics::which(proj$Sample %in% name)
  cells <- proj$cellNames[index]
  sample_subset <- proj[cells]
  df <- getCellColData(sample_subset, select = c("log10(nFrags)", "TSSEnrichment"))
  p <- ggPoint(
    x = df[, 1], y = df[, 2], 
    colorDensity = TRUE, # should the density of points on the plot be indicated by color?
    continuousSet = "sambaNight", 
    xlabel = "Log10 unique fragments",
    ylabel = "TSS enrichment",
    title = paste0("Sample: ", name),
    xlim = c(log10(500), quantile(df[,1], probs = 0.99)),
    ylim = c(0, quantile(df[,2], probs = 0.99))
    ) + geom_hline(yintercept = 4, lty = "dashed") +
    geom_vline(xintercept = 3, lty = "dashed")
  list(plot = p, name = name)
})

gridExtra::grid.arrange(three_samples[[1]]$plot, three_samples[[2]]$plot, 
                        three_samples[[3]]$plot, ncol = 3)

We want a TSS enrichment score of > 4 and a number of unique fragments > 1000 (log10(1000) = 3).

Plotting sample statistics

  • when we have distinct samples, it can be important to compare various metric between samples
  • ridge plots & violin plots are used for grouped data

p1 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_")) %>% 
  ggplot() +
  geom_density(aes(x = TSSEnrichment, fill = Sample), alpha = 0.8) 
## Warning: `as_data_frame()` was deprecated in tibble 2.0.0.
## Please use `as_tibble()` instead.
## The signature and semantics have changed, see `?as_tibble`.

p2 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_")) %>% 
  ggplot() +
  ggridges::geom_density_ridges(aes(x = TSSEnrichment, y = Sample,
                                    fill = Sample), alpha = 0.8) +
  theme(legend.position = "none")

p3 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_")) %>% 
  ggplot() +
  geom_violin(aes(x = Sample, y = TSSEnrichment, fill = Sample), alpha = 0.8) +
  geom_boxplot(aes(x = Sample, y = TSSEnrichment,fill = Sample), alpha = 0.4) + 
  theme(legend.position = "none") +
  labs(title = "TSS Enrichment")


cowplot::plot_grid(p3, p2, p1, ncol = 3)

p1 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>% 
  ggplot() +
  geom_density(aes(x = log10_nFrags, fill = Sample), alpha = 0.8)

p2 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>% 
  ggplot() +
  ggridges::geom_density_ridges(aes(x = log10_nFrags, y = Sample,
                                    fill = Sample), alpha = 0.8) +
  theme(legend.position = "none")

p3 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>% 
  ggplot() +
  geom_violin(aes(x = Sample, y = log10_nFrags, fill = Sample), alpha = 0.8) +
  geom_boxplot(aes(x = Sample, y = log10_nFrags,fill = Sample), alpha = 0.4) + 
  theme(legend.position = "none") +
  labs(title = "number of fragments")


cowplot::plot_grid(p3, p2, p1, ncol = 3)

Plot Fragment Size Distribution & TSS Enrichment Profiles

  • the distribution of fragments size can be very different between samples, cell types and batches -> these differences do not necessarily correlate with differences in quality
  • the dip is the fragment size of a nucleosome ~147bp
  • TSS enrichment profiles
    • clear peak in the center
    • smaller shoulder peak right of the center caused by well positioned +1 nucleosome
p1 <- plotFragmentSizes(ArchRProj = proj)
p2 <- plotTSSEnrichment(ArchRProj = proj)
ggAlignPlots(p1, p2, type = "v")

Filtering Doublets

With the function addDoubleScores() information on predicted doublets has been added. Filter the putative doublets. They are not removed physically, but excluded from downstream analysis. ArchR automatically prints the number of cells removed from each sample and the corresponding percentage which is very handy.

arguments:

  • cutEnrich = minimum cutoff for DoubletEnrichment, number of simulated doublets divided by expected number given a random uniform distribution
  • cutScore = minimum cutoff for Doublet Score, represents -log10(binomial adjusted p-value) for the DoubletEnrichmentadd
  • filterRatio = maximum ratio of predicted doublets to filter based on number of pass-filter cells (A higher filterRatio means that more cells are removed) e.g. 5000 cells

maximum would be filterRatio * 5000 / 100000 = filterRatio * 5000 * 0.05

This way samples with different percentage of doublets will be filtered accordingly.

# in our case we now have 10 251 cells as opposed to 10 661 cells before
# filtering -> 410 cells were removed (3.85%)
proj <- filterDoublets(ArchRProj = proj)

Dimensionality reduction & Clustering

  • two other algorithms:
    • latent semantic indexing (LSI) in Signac
    • landmark diffusion maps (LDM) in SnapATAC
  • ArchR: optimized iterative LSI method
    • exhibits less susceptibility to batch effects
    • focuses on most variable features
    1. create a LSI Reduction from a subset of the total cells
    2. linearly project the remaining cells into this subspace with LSI projection (based on SVD)

Because we can have maximally two accessible alleles per cell, the scATAC-seq data is sparse. Therefore, the majority of accessible regions are not transposed, meaning that most loci will have 0 accessible alleles. A zero could mean “non-accessible” or “not sampled”. For many analysis we can use a binarized matix. Imporantly, the 1s have information, BUT the 0s do not!

A PCA would result in high inter-cell similarity at all 0 positions. An alternative approach for dimensionality reduction is a layered dimensionality reduction. First, Latent Semantic Indexing (LSI) is used. LSI is an approach from language processing. Different samples are the “documents” and different regions/peaks are the “words”.

Iterative LSI

  1. compute term frequency (depth normalization to 10,000 per single cell)

\(TF = \frac{C_{ij}}{F_{j}}\) with \(C_{ij}\) being the total number of counts for peak i in cell j and \(F_{j}\) being the total number of counts in cell j.

  1. Inverse document frequency
  • weights features by how often they occur
  • more weight to less frequent peaks

\(IDF = \frac{N}{n_{p}}\) with N being the total number of cells in the dataset and \(n_{p}\) being the total number of coutns for peak i across all cells.

  1. The term frequency TF is normalized by the inverse document frequncy IDF. You get a TF-IDF matrix (term frequency-inverse document frequency) which tells us how important a region/peak is to a sample. In other words you transform a binary matrix to a non-binary matrix.

\(TF-IDF = \log{1 + (TF * IDF) 10^{4}}\)

  1. SVD identifies the most valuable information across samples. Then we can use these most valuable features to represent the data in a lower dimensional space
  2. Clusters are identified with Seurat’s Shared Nearest Neighbor clustering
  3. Sum accessibility across all single cells in each cluster -> log-normalize
  4. Identify most variable features across the clusters
  5. repeat with most variable peaks as features

With LSI we can reduce the dimensionality of the sparse insertion matrix to tens or hundreds. Then UMAP or t-SNE can be used to visualize the data

Unlike in scRNA-seq we cannot select the top highly variable features before dimensionality reduction (high noise, low reproducibility). Rather the iterative LSI approach first computes a LSI on the most accessible tiles (this will identify clusters corresponding to the major cell types). Then, ArchR computes the average accessibility across these clusters across all features. Next, the most variable peaks across these clusters are identified. The most highly accessible peaks are the features of a new round of LSI. We can set how many rounds of LSI we want to be peformed.

Using iterative LSI reduces batch effects. If you see some batch effects you could try to add more LSI iterations and start from a lower initial clustering resolution. Also, the number of variable features can be lowered. #

proj <- addIterativeLSI(ArchRProj = proj, useMatrix = "TileMatrix", name = "IterativeLSI")

Clustering

Calling clusters in this new space uses the Seurat’s graph clustering function as default clustering method. The Seurat method first computs KNN graph and then a modularity optimization technique to cluster the cells (iteratively group cells together with Louvian algorithm using 10 random starts). Another option is to use “Scran”. The default number of nearest neighbors used is 10. The minimum number of cells for a cluster to be called a cluster is set to 5 by default. The maximum number of clusters to be called is set to 25 by default.

proj <- addClusters(input = proj, reducedDims = "IterativeLSI")
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 10250
## Number of edges: 473760
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8640
## Number of communities: 13
## Elapsed time: 1 seconds

Visualizing a UMAP embedding

  • uses uwot package
  • various attributes of the data can be visualized
    • these are stored in a matrix called cellColData
    • which variable the plot is colored by is specified by colorBy and name parameter
proj <- addUMAP(ArchRProj = proj, reducedDims = "IterativeLSI")
## Warning in sprintf(gettext(fmt, domain = domain), ...): one argument not used by
## format 'invalid uid value replaced by that for user 'nobody''
## Warning: invalid uid value replaced by that for user 'nobody'
## Warning in sprintf(gettext(fmt, domain = domain), ...): one argument not used by
## format 'invalid gid value replaced by that for user 'nobody''
## Warning: invalid gid value replaced by that for user 'nobody'
df <- as_data_frame(cbind(getCellColData(proj), getEmbedding(proj)) ) %>%
  rename(c(umap1 = IterativeLSI.UMAP_Dimension_1, umap2  = IterativeLSI.UMAP_Dimension_2))

variables <- c("Clusters", "Sample", "nFrags", "DoubletScore")

plots1 <- map(c("Clusters", "Sample"), function(n){
  ggplot() +
  geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"), 
                 col = df %>% pull(n)), size = .04) +
    guides(col=guide_legend(title=paste0(n))) +
    xlab("umap1") +
    ylab("umpa2") +
    labs(title = paste0(n))
})

plots2 <- map(c("nFrags", "DoubletScore"), function(n){
  ggplot() +
  geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"), 
                 col = df %>% pull(n)), size = .04) +
    scale_color_viridis_c() +
    guides(fill=guide_legend(title=paste0(n))) +
    xlab("umap1") +
    ylab("umpa2") +
    labs(title = paste0(n))
})

do.call(gridExtra::grid.arrange, c(plots1, ncol=2))#, nrow = 2))

Cluster assignment using gene scores

For the toy dataset marker genes of known hematopoietic regulators can be used. Using MAGIC we add imputation weights to smooth the dropout noise in the gene scores

proj <- addImputeWeights(proj)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
markerGenes  <- c(
    "CD34",  #Early Progenitor
    "GATA1", #Erythroid
    "PAX5", "MS4A1", "MME", #B-Cell Trajectory
    "CD14", "MPO", #Monocytes
    "CD3D", "CD8A"#TCells
  )
p <- plotEmbedding(
    ArchRProj = proj, 
    colorBy = "GeneScoreMatrix", 
    name = markerGenes, 
    embedding = "UMAP",
    imputeWeights = getImputeWeights(proj)
)
do.call(gridExtra::grid.arrange, c(p, ncol = 3))

Visualizing Genome Browser Tracks

Browse local chromatin accessibility at marker genes. Plot genome browser tracks per cluster

p <- plotBrowserTrack(
    ArchRProj = proj, 
    groupBy = "Clusters", 
    geneSymbol = markerGenes, 
    upstream = 50000,
    downstream = 50000
)
## GRanges object with 9 ranges and 2 metadata columns:
##       seqnames              ranges strand |     gene_id      symbol
##          <Rle>           <IRanges>  <Rle> | <character> <character>
##   [1]     chr1 208059883-208084683      - |         947        CD34
##   [2]     chrX   48644982-48652717      + |        2623       GATA1
##   [3]     chr9   36838531-37034476      - |        5079        PAX5
##   [4]    chr11   60223282-60238225      + |         931       MS4A1
##   [5]     chr3 154741913-154901518      + |        4311         MME
##   [6]     chr5 140011313-140013286      - |         929        CD14
##   [7]    chr17   56347217-56358296      - |        4353         MPO
##   [8]    chr11 118209789-118213459      - |         915        CD3D
##   [9]     chr2   87011728-87035519      - |         925        CD8A
##   -------
##   seqinfo: 24 sequences from hg19 genome
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
grid::grid.newpage()
grid::grid.draw(p$CD14)

Integration with scRNA-seq

  • the scATAC-seq gene score matrix is compared with the scRNA-seq gene expression matrix
  • for this alignment the FindTransferAnchors() function from Seurat is used
  • to scale for large sample size, this process is parallelized
  • for each cell in ATAC we find the cell in scRNA-seq that looks most similar -> assign the correpsonding gene expression to that cell.

Apart from using this information for identifying clusters we can also use it for identifying predicted cis-regulatory elements.

if(!file.exists("scRNA-Hematopoiesis-Granja-2019.rds")){
    download.file(
        url = "https://jeffgranja.s3.amazonaws.com/ArchR/TestData/scRNA-Hematopoiesis-Granja-2019.rds",
        destfile = "scRNA-Hematopoiesis-Granja-2019.rds"
    )
}

# ranged summarized Experiment
seRNA <- readRDS("scRNA-Hematopoiesis-Granja-2019.rds")
seRNA
## class: RangedSummarizedExperiment 
## dim: 20287 35582 
## metadata(0):
## assays(1): counts
## rownames(20287): FAM138A OR4F5 ... S100B PRMT2
## rowData names(3): gene_name gene_id exonLength
## colnames(35582): CD34_32_R5:AAACCTGAGTATCGAA-1
##   CD34_32_R5:AAACCTGAGTCGTTTG-1 ...
##   BMMC_10x_GREENLEAF_REP2:TTTGTTGCATGTGTCA-1
##   BMMC_10x_GREENLEAF_REP2:TTTGTTGCATTGAAAG-1
## colData names(10): Group nUMI_pre ... BioClassification Barcode

Lets have a look at the count matrix:

# sparse count matrix of scRNA-seq
assays(seRNA)[[1]][1:10, 1:5]
## 10 x 5 sparse Matrix of class "dgCMatrix"
##            CD34_32_R5:AAACCTGAGTATCGAA-1 CD34_32_R5:AAACCTGAGTCGTTTG-1
## FAM138A                                .                             .
## OR4F5                                  .                             .
## AL627309.1                             .                             .
## OR4F29                                 .                             .
## OR4F16                                 .                             .
## FAM87B                                 .                             .
## LINC00115                              .                             .
## FAM41C                                 1                             .
## AL645608.2                             .                             .
## SAMD11                                 .                             .
##            CD34_32_R5:AAACCTGGTTCCACAA-1 CD34_32_R5:AAACGGGAGCTTCGCG-1
## FAM138A                                .                             .
## OR4F5                                  .                             .
## AL627309.1                             .                             .
## OR4F29                                 .                             .
## OR4F16                                 .                             .
## FAM87B                                 .                             .
## LINC00115                              .                             .
## FAM41C                                 1                             .
## AL645608.2                             .                             .
## SAMD11                                 .                             .
##            CD34_32_R5:AAACGGGAGGGAGTAA-1
## FAM138A                                .
## OR4F5                                  .
## AL627309.1                             .
## OR4F29                                 .
## OR4F16                                 .
## FAM87B                                 .
## LINC00115                              .
## FAM41C                                 .
## AL645608.2                             .
## SAMD11                                 .

Metadata of the scRNA-seq dataset:

We already have clustering, umap embeddings and cell types.

colData(seRNA) %>% head %>% knitr::kable()
Group nUMI_pre nUMI nGene initialClusters UMAP1 UMAP2 Clusters BioClassification Barcode
CD34_32_R5:AAACCTGAGTATCGAA-1 CD34_D2T1 17876 8303 3187 Cluster1 -6.113410 4.616498 Cluster5 05_CMP.LMPP AAACCTGAGTATCGAA-1
CD34_32_R5:AAACCTGAGTCGTTTG-1 CD34_D2T1 9277 3917 1787 Cluster2 -8.800932 -1.228907 Cluster8 08_GMP.Neut AAACCTGAGTCGTTTG-1
CD34_32_R5:AAACCTGGTTCCACAA-1 CD34_D2T1 13073 6023 2552 Cluster3 -9.723482 7.335178 Cluster1 01_HSC AAACCTGGTTCCACAA-1
CD34_32_R5:AAACGGGAGCTTCGCG-1 CD34_D2T1 8412 4493 2191 Cluster4 -4.293071 5.692705 Cluster6 06_CLP.1 AAACGGGAGCTTCGCG-1
CD34_32_R5:AAACGGGAGGGAGTAA-1 CD34_D2T1 11914 5190 2322 Cluster3 -7.989706 9.108693 Cluster1 01_HSC AAACGGGAGGGAGTAA-1
CD34_32_R5:AAACGGGAGTTACGGG-1 CD34_D2T1 12075 4634 2152 Cluster3 -7.271959 6.980415 Cluster1 01_HSC AAACGGGAGTTACGGG-1

Plot Quality Metrics of the scRNA-seq dataset:

as_data_frame(colData(seRNA)) %>% 
  select(nUMI, nGene, Group) %>% 
  pivot_longer(cols = !Group, names_to = "stat") %>% 
  ggplot() +
  geom_violin(aes(x = Group, y = value, fill = Group)) +
  facet_wrap(~stat, scales = "free") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        legend.position = "none") +
  xlab("Sample")

  #pivot_longer(cols = !s)
df <- as_data_frame(colData(seRNA))

p1 <- ggplot() +
geom_point(aes(x = df %>% pull("UMAP1"), y = df %>% pull("UMAP2"), 
               col = df %>% pull("BioClassification")), size = .04) +
  guides(col=guide_legend(title="CellType")) +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "scRNA-seq dataset - cell type")

p2 <- ggplot() +
geom_point(aes(x = df %>% pull("UMAP1"), y = df %>% pull("UMAP2"), 
               col = df %>% pull("nGene")), size = .04) +
  guides(col=guide_legend(title="number of genes")) +
  scale_color_viridis_c() +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "scRNA-seq dataset - gene number")

gridExtra::grid.arrange(p1, p2, ncol = 1)

LS0tCnRpdGxlOiAiQXJjaFJfcGlwZWxpbmUiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiA1CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlCi0tLQoKPHN0eWxlPgpib2R5IHsKdGV4dC1hbGlnbjoganVzdGlmeX0KPC9zdHlsZT4KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBGQUxTRSwgYXV0b2RlcCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvQXJjaFIiKQpzZXR3ZCgiL29taWNzL2dyb3Vwcy9PRTA1MzMvaW50ZXJuYWwva2F0aGFyaW5hL3NjRG9SSS9BcmNoUi8iKQoKc2V0LnNlZWQoMSkKYGBgCgoKCkFyY2hSIHRha2VzIGFzIGlucHV0IGFsaWduZWQgQkFNIG9yIGZyYWdtZW50IGZpbGVzLiBUaGVzZSBmaWxlcyBhcmUgc3RvcmVkIGluIHRoZSAKSERGNSBmaWxlIGZvcm1hdCAoaGllcmFyY2hpY2FsIGRhdGEgZm9ybWF0IHZlcnNpb241KS4gVGhlIEhERjUgZmlsZXMgYXJlIHRoZSAKY29uc3RpdHVlbnQgcGllY2VzIG9mIGFuIEFyY2hSIGFuYWx5c2lzLiBUaGV5IGFyZSBjYWxsZWQgQXJyb3cgZmlsZXMuIEFsbCBBcnJvdwpmaWxlcyBhcmUgZ3JvdXBlZCBpbnRvIGEgcHJvamVjdCwgYSBjb21wcmVzc2VkIFIgZGF0YSBmaWxlLiBUaGUgRmlsZXMgYXJlIGFjY2Vzc2VkIAppbiBtaW5pbWFsIGNodW5rcyAocGFyYWxsZWwgcmVhZCBhbmQgd3JpdGUgb3BlcmF0aW9ucykuIFRoZXJlZm9yZSwgaW4gbWVtb3J5IHdlCmRvIG5vdCBoYXZlIGFueSBsYXJnZSBmaWxlIHNpemVzLgoKVG8gc2VsZWN0IGhpZ2ggcXVhbGl0eSBjZWxscyBUU1MgZW5yaWNobWVudCBzY29yZXMgYXJlIHVzZWQuIAoKCiAgCiMgRmlyc3Qgc3RlcHMgaW4gQXJjaFIKICAKIyMgTG9hZCBsaWJyYXJpZXMKICAKYGBge3J9CmxpYnJhcnkoQXJjaFIpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkocmhkZjUpCmxpYnJhcnkodXdvdCkKbGlicmFyeSh0aWR5dmVyc2UpCiNsaWJyYXJ5KGNhcmV0KQpoNWRpc2FibGVGaWxlTG9ja2luZygpCmBgYAogIAogIApgYGB7cn0KaW5wdXRGaWxlcyA8LSBnZXRUdXRvcmlhbERhdGEoIkhlbWF0b3BvaWVzaXMiKQpgYGAKCgpgYGB7cn0KYWRkQXJjaFJHZW5vbWUoImhnMTkiKQpgYGAKCiMjIENyZWF0ZSBBcnJvdyBGaWxlcwoKMS4gcmVhZCBhY2Nlc3NpYmxlIGZyYWdtZW50cyAKMi4gY2FsY3VsYXRlIFFDIGluZm9ybWF0aW9uIGZvciBlYWNoIGNlbGwgKFRTUyBlbnJpY2htZW50IHNjb3JlcyBhbmQgbnVjZWxvc29tZSBpbmZvKQozLiBmaWx0ZXIgY2VsbHMgYmFzZWQgb24gUUMgcGFyYW1ldGVycwo0LiBjcmVhdGUgZ2Vub21lLXdpZGUgdGlsZSBtYXRyaXggdXNpbmcgNTAwLWJwIGJpbnMKNS4gY3JlYXRlIEdlbmVTY29yZU1hdHJpeCB1c2luZyBnZW5lIGFubm90YXRpb24KCgpgYGB7cn0KQXJyb3dGaWxlcyA8LSBjcmVhdGVBcnJvd0ZpbGVzKAogIGlucHV0RmlsZXMgPSBpbnB1dEZpbGVzLAogIHNhbXBsZU5hbWVzID0gbmFtZXMoaW5wdXRGaWxlcyksCiAgbWluVFNTID0gNCwgI0RvbnQgc2V0IHRoaXMgdG9vIGhpZ2ggYmVjYXVzZSB5b3UgY2FuIGFsd2F5cyBpbmNyZWFzZSBsYXRlcgogIG1pbkZyYWdzID0gMTAwMCwgIyBtaW5pbXVtIG51bWJlciBvZiBtYXBwZWQgZnJhZ21lbnRzIHJlcXVpcmVkCiAgIyBjb3VudCBtYXRyaXgsIGluc3RlYWQgb2YgdXNpbmcgcGVhayBpdCB1c2VzIGZpeGVkLXdpZHRoIHNsaWRpbmcgd2luZG93IG9mIGJpbnMgYWNyb3NzIHRoZSB3aG9sZSBnZW5vbWUKICBhZGRUaWxlTWF0ID0gVFJVRSwgCiAgYWRkR2VuZVNjb3JlTWF0ID0gVFJVRSwgIyB1c2VzIHNpZ25hbCBwcm94aW1hbCB0byB0aGUgVFNTIHRvIGVzdGltYXRlIGdlbmUgYWN0aXZpdHkKICBzdWJUaHJlYWRpbmcgPSBGQUxTRSwKICBtYXhGcmFncyA9IDFlKzA1LAogIG1pbkZyYWdTaXplID0gMTAsCiAgbWF4RnJhZ1NpemUgPSAyMDAwLAogIFFDRGlyID0gIlF1YWxpdHlDb250cm9sIiwKICAjIHRoZSBsZW5ndGggaW4gYnAgdGhhdCB3cmFwcyBhcm91bmQgbnVjbGVvc29tZXMgLT4gCiAgICAjaWRlbnRpZnkgZnJhZ21lbnRzIGFzIHN1Yi1udWNsZW9zb21lIHNwYW5uaW5nLCBtb25vLW51Y2xlb3NvbWUgc3Bhbm5pbmcgb3IgbXVsdGktbnVjbGVvc29tZSBzcGFubmluZwogIG51Y0xlbmd0aCA9IDE0NywgCiAgIyBpbnRlZ2VyIHZlY3RvciAtPiBkZWZpbmUgcmVnaW9uIHVwL2Rvd25zdHJlYW0gb2YgVFNTIHRvIGluY2x1ZGUgYXMgcHJvbW90ZXIgcmVnaW9uCiAgIyBjYW4gYmUgdXNlZCB0byBjYWxjdWxhdGUgZS5nIGZyYWN0aW9uIG9mIHJlYWRzIGluIHByb21vdGVyIChGSVApCiAgcHJvbW90ZXJSZWdpb24gPSBjKDIwMDAsIDEwMCksCiAgIyBwYXJhbWV0ZXJzIGZvciBjb21wdXRpbmcgVFNTIGVucmljaG1lbnQgc2NvcmVzLCB3aW5kb3cgKGJwKSBjZW50ZXJlZCBhdCBUU1MgPSAxMDEKICAjIGZsYW5raW5nIHdpbmRvdyA9IDIwMDAgYnAKICAjIG5vcm0gPSBzaXplIG9mIGZsYW5rIHdpbmRvdyB1c2VkIGZvciBub3JtYWxpemF0aW9uID0gMTAwIGJwCiAgIyBhY2Nlc3NpYmlsaXR5IHdpdGhpbiAxMDEgYnAgc3Vycm91bmRpbmcgdGhlIFRTUyB3aWxsIGJlIG5vcm1hbGl6ZWQgdG8gYWNjZXNzaWJpbGl0eQogICMgaW4gMTAwIGJwIGJpbnMgZnJvbSAtMjAwMDotMTkwMWJwIGFuZCAxOTAxOiAyMDAwCiAgVFNTUGFyYW1zID0gbGlzdCgxMDEsIDIwMDAsIDEwMCksCiAgIyB3aGljaCBjaHJvbW9zb21lcyB0byBleGNsdWRlIGZvcm0gZG93bnN0cmVhbSBhbmFseXNpcwogICMgaW4gaHVtYW4gYW5kIG1vdXNlOiBtaXRvY2hvbmRyaWFsIEROQSAoY2hyTSkgYW5kIG1hbGUgc2V4IGNocm9tb3NvbWUgKGNoclkpCiAgIyB0aGUgZnJhZ21lbnRzIGFyZSBzdGlsbCBzdG9yZWQgaW4gdGhlIGFycm93IGZpbGVzCiAgZXhjbHVkZUNociA9IGMoImNock0iLCAiY2hyWSIpLAogICMgbnVtYmVyIG9mIGNodW5rcyB0byBkaXZpZGUgY2hyb21vc29tZXMgaW4gLT4gbG93LW1lbW9yeSBwYXJhbGxlbGl6ZWQgcmVhZGluZyBvZiBpbnB1dCBmaWxlcwogIG5DaHVuayA9IDUsCiAgIyBuYW1lIG9mIGZpZWxkIGluIGlucHV0IGJhbSBmaWxlIGNvbnRhaW5pbmcgdGhlIGJhcmNvZGUgdGFnIGluZm9ybWF0aW9uCiAgYmNUYWcgPSAicW5hbWUiLAogIG9mZnNldFBsdXMgPSA0LCAjIG9mZnNldCBhcHBsaWVkIHRvICsgc3RyYW5kZWQgVG41IGluc2VydGlvbiAtPiBhY2NvdW50IGZvciBwcmVjaXNlIFRuNSBiaW5kaW5nIHNpdGUKICBvZmZzZXRNaW51cyA9IC01LCAKICBsb2dGaWxlID0gY3JlYXRlTG9nRmlsZSgiY3JlYXRlQXJyb3dzIikKKQpgYGAKCiMjIFF1YWxpdHkgQ29udHJvbAoKSGF2ZSBhIGxvb2sgYXQgdGhpcyBmb3IgYWRkaXRpb25hbCBRQyEKaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0FUQUNzZXFRQy9pbnN0L2RvYy9BVEFDc2VxUUMuaHRtbAoKMS4gdGhlIG51bWJlciBvZiAqKnVuaXF1ZSBudWNsZWFyIGZyYWdtZW50cyoqIChhcyBvcHBvc2VkIHRvIG1pdG9jaG9uZHJpYWwgZnJhZ21lbnRzKQpBIGNlbGwgd2l0aCB2ZXJ5IGZldyB1c2FibGUgZnJhZ21lbnRzIHdpbGwgbm90IHByb3ZpZGUgZW5vdWdoIGRhdGEgdG8gbWFrIHVzZWZ1bCBjb25jbHVzaW9ucy4KMi4gKipzaWduYWwtdG8tYmFja2dyb3VuZCByYXRpbyoqIC0+IGlmIHRoaXMgaXMgbG93IHRoaXMgcHJvYmFibHkgY29ycmVzcG9uZHMgdG8gZHlpbmcKY2VsbHMgd2hlcmUgdGhlIGVudGlyZSBnZW5vbWUgYWxsb3dzIHJhbmRvbSB0cmFuc3Bvc2l0aW9uCjMuICoqZnJhZ21lbnQgc2l6ZSBkaXN0cmlidXRpb24qKiAtPiBzaW5jZSAxNDcgYnAgYXJlIHdyYXBwZWQgYXJvdW5kIGEgbnVjbGVvc29tZSBpdCBpcyAKZXhwZWN0ZWQgdGhhdCB0aGVyZSBhcmUgZGVwbGV0aW9ucyBvZiBmcmFnbWVudHMgb2YgdGhpcyBsZW5ndGggYXQgcmVndWxhciBpbnRlcnZhbHMuIApXZSBleHBlY3QgdG8gc2VlIGEgcGVyaW9kaWMgZGlzdHJpYnV0aW9uIG9mIGZyYWdtZXRuIHNpemUgY29ycmVzcG9uZGluZyB0byBudWNsZW9zb21lcwoobW9ubywgZGksIHRyaSwgLi4uKSwgYmVjYXVzZSBUbjUgY2Fubm90IGN1dCBETkEgdGhhdCBpcyB0aWdodGx5IHdyYXBwZWQgYXJvdW5kIAphIG51Y2xlb3NvbWUuIAoKCiMjIEluZmVycmluZyBEb3VibGV0cwoKU2hvdWxkIGJlIHJlbW92ZWQsIGJlY2F1c2UgdGhleSBjYW4gaW50ZXJmZXJlIHdpdGggZG93bnN0cmVhbSBhbmFseXNpcy4KCioqRG91YmxldCBkZXRlY3Rpb24tYW5kLXJlbW92YWwgYWxnb3JpdGhtOioqCkhldGVyb3R5cGljIGRvdWJsZXRzIGFyZSBpZGVudGlmaWVkIGJ5IGdlbmVyYXRpbmcgYSBjb2xsZWN0aW9uIG9mIHN5bnRoZXRpYyBkb3VibGV0cy4KVGhlc2Ugc3ludGhldGljIGRvdWJsZXRzIGFyZSBwcm9qZWN0ZWQgaW50byBsb3ctZGltZW5zaW9uYWwgZW1iZWRkaW5ncy4gU2VhcmNoaW5nCmZvciBuZWFyZXN0IG5laWdoYm91cnMgdG8gdGhlIHN5bnRoZXRpYyBkb3VibGV0cyB3ZSBjYW4gaWRlbnRpZnkgZG91YmxldHMgaW4gdGhlCmRhdGFzZXQuIFRoaXMgb3V0cGVyZm9ybXMgdGhlIHByZWRpY3Rpb24gb2YgZG91YmxldHMgdXNpbmcgZnJhZ21lbnQgbnVtYmVyCihST0MtQVVDKS4gKENvbXBhcmVkIHRvIGRlbXV4bGV0IGFzIGdyb3VuZCB0cnV0aCkKCioqV2UgY2FuIGFsc28gaWRlbnRpZnkgZG91YmxldHMgaW4gdGhlIHNjUk5BLXNlcSBzcGFjZSBpZiB3ZSBoYXZlIHBhaXJlZCBkYXRhCmFuZCByZW1vdmUgdGhlIGNlbGxzIGluIHRoaXMgd2F5LioqCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodD01LCByZXN1bHRzPSAiYXNpcyJ9CiMgZm9yIGVhY2ggc2FtcGxlIHByb3ZpZGVkIGRvdWJsZXQgaW5mb3JtYXRpb24gd2lsbCBiZSBhc3NpZ25lZCB0byBlYWNoIGNlbGwKIyB0aGlzIHdheSB3ZSBjYW4gcmVtb3ZlIGRvdWJsZXQtYmFzZWQgY2x1c3RlcnMgZG93bnN0cmVhbQpkb3ViU2NvcmVzIDwtIGFkZERvdWJsZXRTY29yZXMoCiAgdXNlTWF0cml4ID0gIlRpbGVNYXRyaXgiLAogIGlucHV0ID0gQXJyb3dGaWxlcywKICBrID0gMTAsICNSZWZlcnMgdG8gaG93IG1hbnkgY2VsbHMgbmVhciBhICJwc2V1ZG8tZG91YmxldCIgdG8gY291bnQuCiAgblRyaWFsID0gNSwgIyBudW1iZXIgb2YgdGltZSB0byBzaW11bGF0ZSBuQ2VsbCBkb3VibGV0cyAKICBrbm5NZXRob2QgPSAiVU1BUCIsICNSZWZlcnMgdG8gdGhlIGRpbWVuc2lvbmFsaXR5IHJlZHVjaXRvbiBtZXRob2QgdG8gdXNlIGZvciBuZWFyZXN0IG5laWdoYm9yIHNlYXJjaC4KICBMU0lNZXRob2QgPSAxICMgb2RlciBvZiBub3JtYWxpemF0aW9uOiB0Zi1sb2coaWRmKQopCgojIHRoZSBkb3VibGV0IGluZm9ybWF0aW9uIGlzIHNhdmVkIGluIGEgc2ltcGxlTGlzdG9iamVjdAojIHJlYWQgaW4gdGhlIG9iamVjdApkb3VibGV0X3N1bW1hcnkgPC0gcmVhZFJEUygiUXVhbGl0eUNvbnRyb2wvc2NBVEFDX0JNTUNfUjEvc2NBVEFDX0JNTUNfUjEtRG91YmxldC1TdW1tYXJ5LnJkcyIpCgpkb3VibGV0X3N1bW1hcnlbWzJdXSAlPiUgaGVhZCgpICU+JSBrYWJsZSgpCgojIGdldCBmaXJzdCBlbnRyeSBvZiB0aGUgbGlzdCA9IG9yaWdpbmFsRGF0YVVNQVAKcDEgPC0gZG91YmxldF9zdW1tYXJ5IFtbMV1dICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbCA9IGVucmljaG1lbnQpLCBzaXplID0gLjEpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJEb3VibGV0RW5yaWNobWVudCIpKSArCiAgbGFicyh0aXRsZSA9ICJTaW11bGF0ZWQgRG91YmxldCBFbnJpY2htZW50IG92ZXIgZXhwZWN0YXRpb24iKQoKcDIgPC0gZG91YmxldF9zdW1tYXJ5IFtbMV1dICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbCA9IHNjb3JlKSwgc2l6ZSA9IC4xKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iRG91YmxldFNjb3JlcyAtbG9nMTAoUC1hZGopIikpICsKICBsYWJzKHRpdGxlID0gIkRvdWJsZXQgU2NvcmVzIC1sb2cxMChQLWFkaikiKQoKCnAzIDwtIGRvdWJsZXRfc3VtbWFyeVtbMl1dICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgsIHkgPSB5LCBjb2wgPSBkZW5zaXR5KSwgc2l6ZSA9IC4xKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iU2ltdWxhdGVkIERvdWJsZXQgRGVuc2l0eSIpKSArCiAgbGFicyh0aXRsZSA9ICJEb3VibGV0IGRlbnNpdHkiKQoKCnA0IDwtIGRvdWJsZXRfc3VtbWFyeVtbMl1dICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgsIHkgPSB5LCBjb2wgPSBkZW5zaXR5KSwgc2l6ZSA9IC4xKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZG91YmxldF9zdW1tYXJ5W1sxXV0sIGFlcyh4ID0gWDEsIHkgPSBYMiksIHNpemUgPSAuMSwgYWxwaGEgPSAuNCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGU9IlNpbXVsYXRlZCBEb3VibGV0IERlbnNpdHkiKSkgKwogIGxhYnModGl0bGUgPSAiU2ltdWxhdGVkIGRvdWJsZXQgZGVuc2l0eSBvdmVybGF5ZWQiKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEsIHAyLCBwMywgcDQsIG5jb2wgPSAyKQoKYGBgCgoKIyMgQ3JlYXRlIEFyY2hSUHJvamVjdAoKQW4gQXJjaFIgUHJvamVjdCBpcyBpbml0aWFsaXplZCB3aXRoIHNvbWUgaW1wb3J0YW50IGF0dHJpYnV0ZXM6CgoqIG91cHV0IGRpcmVjdG9yeQoqIHNhbXBsZSBuYW1lcwoqIGBzYW1wbGVDb2xEYXRhYCAtPiBtYXRyaXggY29udGFpbmludCBkYXRhIGZvciBlYWNoIHNhbXBsZQoqIGBjZWxsQ29sRGF0YWAgLT4gY29udGFpbnMgZGF0YSBhc3NvY2lhdGVkIHdpdGggZWFjaCBjZWxsCiAgKyBhZnRlciB1c2luZyBgYWRkRG91YmxldFNjb3JlKClgIHRoZXJlIHdpbGwgYmUgYSBjb2x1bW4gCiAgZm9yICJEb3VibGV0IEVucmljaG1lbnQiIGFuZCAiRG91YmxldCBTY29yZSIKKiB0b3RhbCBudW1iZXIgb2YgY2VsbHMgKGV4Y2x1ZGluZyBkb3VibGV0cykKKiBtZWRpYW4gVFNTIHNjb3JlICYgbWVkaWFuIG51bWJlciBvZiBmcmFnbWVudHMgYWNyb3NzIGFsbCBjZWxscyAKYW5kIHNhbXBsZXMKCmBgYHtyfQpwcm9qIDwtIEFyY2hSUHJvamVjdCgKICBBcnJvd0ZpbGVzID0gQXJyb3dGaWxlcywgCiAgb3V0cHV0RGlyZWN0b3J5ID0gIkFyY2hSVmlnbmV0dGUiLAogIGNvcHlBcnJvd3MgPSBUUlVFLCAjVGhpcyBpcyByZWNvbW1lbmVkIHNvIHRoYXQgeW91IG1haW50YWluIGFuIHVuYWx0ZXJlZCBjb3B5IGZvciBsYXRlciB1c2FnZS4KICBnZW5lQW5ub3RhdGlvbiA9IGdldEdlbmVBbm5vdGF0aW9uKCksCiAgI2dlbm9tZUFubm90YXRpb24gPSBnZXRHZW5lQW5ub3RhdGlvbigpLAogIHNob3dMb2dvID0gRkFMU0UKKQpgYGAKCgojIFBsb3QgUUMgbWV0cmljcyAKCkRhdGEgYmVmb3JlIFFDIGFuZCBjb3JyZXNwb25kaW5nIHBsb3RzIGFyZSBzYXZlZCBpbiB0aGUgUXVhbGl0eSBDb250cm9sIG91dHB1dCBmb2xkZXIuCgojIyBsb2cxMCh1bmlxdWUgZnJhZ21lbnRzKSB2cyBUU1MgZW5yaWNobWVudAoKKiBUU1MgZW5yaWNobWVudCBzY29yZSA9IHNpZ25hbC10by1iYWNrZ3JvdW5kIAoqIG51bWJlciBvZiB1bmlxdWUgZnJhZ21lbnRzIC0+IGNlbGxzIHdpdGggdmVyeSBmZXcgZnJhZ21lbnRzIGRvIG5vdCBoYXZlIGVub3VnaCAKZGF0YSB0byBjb25maWRlbnRseSBhbmFseXplIHRoZW0gCiogaW4gdGhlIHBsb3QgYXJlYXMgd2l0aCBtb3JlIHBvaW50cy9jZWxscyBhcmUgY29sb3JlZCBpbiBvcmFuZ2UsIGFuZCBhcmVhcwp3aXRoIGxlc3MgcG9pbnRzIGluIGJsdWUsIGluZGljYXRpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjZWxsCgoKYGBge3IsIGZpZy53aWR0aD04fQojY3JlYXRlIDMgc2VwYXJhdGUgZGF0YWZyYW1lcyBmb3IgYWxsIHNhbXBsZXMKdGhyZWVfc2FtcGxlcyA8LSBtYXAodW5pcXVlKHByb2okU2FtcGxlKSwgZnVuY3Rpb24obmFtZSl7CiAgaW5kZXggPC0gQmlvY0dlbmVyaWNzOjp3aGljaChwcm9qJFNhbXBsZSAlaW4lIG5hbWUpCiAgY2VsbHMgPC0gcHJvaiRjZWxsTmFtZXNbaW5kZXhdCiAgc2FtcGxlX3N1YnNldCA8LSBwcm9qW2NlbGxzXQogIGRmIDwtIGdldENlbGxDb2xEYXRhKHNhbXBsZV9zdWJzZXQsIHNlbGVjdCA9IGMoImxvZzEwKG5GcmFncykiLCAiVFNTRW5yaWNobWVudCIpKQogIHAgPC0gZ2dQb2ludCgKICAgIHggPSBkZlssIDFdLCB5ID0gZGZbLCAyXSwgCiAgICBjb2xvckRlbnNpdHkgPSBUUlVFLCAjIHNob3VsZCB0aGUgZGVuc2l0eSBvZiBwb2ludHMgb24gdGhlIHBsb3QgYmUgaW5kaWNhdGVkIGJ5IGNvbG9yPwogICAgY29udGludW91c1NldCA9ICJzYW1iYU5pZ2h0IiwgCiAgICB4bGFiZWwgPSAiTG9nMTAgdW5pcXVlIGZyYWdtZW50cyIsCiAgICB5bGFiZWwgPSAiVFNTIGVucmljaG1lbnQiLAogICAgdGl0bGUgPSBwYXN0ZTAoIlNhbXBsZTogIiwgbmFtZSksCiAgICB4bGltID0gYyhsb2cxMCg1MDApLCBxdWFudGlsZShkZlssMV0sIHByb2JzID0gMC45OSkpLAogICAgeWxpbSA9IGMoMCwgcXVhbnRpbGUoZGZbLDJdLCBwcm9icyA9IDAuOTkpKQogICAgKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDQsIGx0eSA9ICJkYXNoZWQiKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAzLCBsdHkgPSAiZGFzaGVkIikKICBsaXN0KHBsb3QgPSBwLCBuYW1lID0gbmFtZSkKfSkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHRocmVlX3NhbXBsZXNbWzFdXSRwbG90LCB0aHJlZV9zYW1wbGVzW1syXV0kcGxvdCwgCiAgICAgICAgICAgICAgICAgICAgICAgIHRocmVlX3NhbXBsZXNbWzNdXSRwbG90LCBuY29sID0gMykKCgpgYGAKCldlIHdhbnQgYSBUU1MgZW5yaWNobWVudCBzY29yZSBvZiA+IDQgYW5kIGEgbnVtYmVyIG9mIHVuaXF1ZSBmcmFnbWVudHMgPiAxMDAwIChsb2cxMCgxMDAwKSA9IDMpLiAKCgojIyMgUGxvdHRpbmcgc2FtcGxlIHN0YXRpc3RpY3MKCiogd2hlbiB3ZSBoYXZlIGRpc3RpbmN0IHNhbXBsZXMsIGl0IGNhbiBiZSBpbXBvcnRhbnQgdG8gY29tcGFyZSB2YXJpb3VzCm1ldHJpYyBiZXR3ZWVuIHNhbXBsZXMKKiByaWRnZSBwbG90cyAmIHZpb2xpbiBwbG90cyBhcmUgdXNlZCBmb3IgZ3JvdXBlZCBkYXRhCgpgYGB7cixmaWcud2lkdGg9MTB9CgpwMSA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IFRTU0VucmljaG1lbnQsIGZpbGwgPSBTYW1wbGUpLCBhbHBoYSA9IDAuOCkgCgpwMiA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdncmlkZ2VzOjpnZW9tX2RlbnNpdHlfcmlkZ2VzKGFlcyh4ID0gVFNTRW5yaWNobWVudCwgeSA9IFNhbXBsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcDMgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihhZXMoeCA9IFNhbXBsZSwgeSA9IFRTU0VucmljaG1lbnQsIGZpbGwgPSBTYW1wbGUpLCBhbHBoYSA9IDAuOCkgKwogIGdlb21fYm94cGxvdChhZXMoeCA9IFNhbXBsZSwgeSA9IFRTU0VucmljaG1lbnQsZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC40KSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiVFNTIEVucmljaG1lbnQiKQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMywgcDIsIHAxLCBuY29sID0gMykKYGBgCgoKYGBge3IsZmlnLndpZHRoPTEwfQpwMSA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpLCBsb2cxMF9uRnJhZ3MgPSBsb2cxMChuRnJhZ3MpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IGxvZzEwX25GcmFncywgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KQoKcDIgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSwgbG9nMTBfbkZyYWdzID0gbG9nMTAobkZyYWdzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZ3JpZGdlczo6Z2VvbV9kZW5zaXR5X3JpZGdlcyhhZXMoeCA9IGxvZzEwX25GcmFncywgeSA9IFNhbXBsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcDMgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSwgbG9nMTBfbkZyYWdzID0gbG9nMTAobkZyYWdzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihhZXMoeCA9IFNhbXBsZSwgeSA9IGxvZzEwX25GcmFncywgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gU2FtcGxlLCB5ID0gbG9nMTBfbkZyYWdzLGZpbGwgPSBTYW1wbGUpLCBhbHBoYSA9IDAuNCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIm51bWJlciBvZiBmcmFnbWVudHMiKQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMywgcDIsIHAxLCBuY29sID0gMykKYGBgCgoKIyMjIFBsb3QgRnJhZ21lbnQgU2l6ZSBEaXN0cmlidXRpb24gJiBUU1MgRW5yaWNobWVudCBQcm9maWxlcwoKKiB0aGUgZGlzdHJpYnV0aW9uIG9mIGZyYWdtZW50cyBzaXplIGNhbiBiZSB2ZXJ5IGRpZmZlcmVudCBiZXR3ZWVuIHNhbXBsZXMsCmNlbGwgdHlwZXMgYW5kIGJhdGNoZXMgLT4gdGhlc2UgZGlmZmVyZW5jZXMgZG8gbm90IG5lY2Vzc2FyaWx5IGNvcnJlbGF0ZSB3aXRoIApkaWZmZXJlbmNlcyBpbiBxdWFsaXR5CiogdGhlIGRpcCBpcyB0aGUgZnJhZ21lbnQgc2l6ZSBvZiBhIG51Y2xlb3NvbWUgfjE0N2JwCiogVFNTIGVucmljaG1lbnQgcHJvZmlsZXMKICArIGNsZWFyIHBlYWsgaW4gdGhlIGNlbnRlciAKICArIHNtYWxsZXIgc2hvdWxkZXIgcGVhayByaWdodCBvZiB0aGUgY2VudGVyIGNhdXNlZCBieSB3ZWxsIHBvc2l0aW9uZWQgKzEgbnVjbGVvc29tZQoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTV9CnAxIDwtIHBsb3RGcmFnbWVudFNpemVzKEFyY2hSUHJvaiA9IHByb2opCnAyIDwtIHBsb3RUU1NFbnJpY2htZW50KEFyY2hSUHJvaiA9IHByb2opCmdnQWxpZ25QbG90cyhwMSwgcDIsIHR5cGUgPSAidiIpCmBgYAoKIyBGaWx0ZXJpbmcgRG91YmxldHMKCldpdGggdGhlIGZ1bmN0aW9uIGBhZGREb3VibGVTY29yZXMoKWAgaW5mb3JtYXRpb24gb24gcHJlZGljdGVkIGRvdWJsZXRzIGhhcyBiZWVuCmFkZGVkLiBGaWx0ZXIgdGhlIHB1dGF0aXZlIGRvdWJsZXRzLiBUaGV5IGFyZSBub3QgcmVtb3ZlZCBwaHlzaWNhbGx5LCBidXQgCmV4Y2x1ZGVkIGZyb20gZG93bnN0cmVhbSBhbmFseXNpcy4gQXJjaFIgYXV0b21hdGljYWxseSBwcmludHMgdGhlIG51bWJlciBvZgpjZWxscyByZW1vdmVkIGZyb20gZWFjaCBzYW1wbGUgYW5kIHRoZSBjb3JyZXNwb25kaW5nCnBlcmNlbnRhZ2Ugd2hpY2ggaXMgdmVyeSBoYW5keS4KCioqYXJndW1lbnRzOioqCgoqIGN1dEVucmljaCA9IG1pbmltdW0gY3V0b2ZmIGZvciBEb3VibGV0RW5yaWNobWVudCwgbnVtYmVyIG9mIHNpbXVsYXRlZCAKZG91YmxldHMgZGl2aWRlZCBieSBleHBlY3RlZCBudW1iZXIgZ2l2ZW4gYSByYW5kb20gdW5pZm9ybSBkaXN0cmlidXRpb24KKiBjdXRTY29yZSA9IG1pbmltdW0gY3V0b2ZmIGZvciBEb3VibGV0IFNjb3JlLCByZXByZXNlbnRzIC1sb2cxMChiaW5vbWlhbCBhZGp1c3RlZCBwLXZhbHVlKQpmb3IgdGhlIERvdWJsZXRFbnJpY2htZW50YWRkCiogZmlsdGVyUmF0aW8gPSBtYXhpbXVtIHJhdGlvIG9mIHByZWRpY3RlZCBkb3VibGV0cyB0byBmaWx0ZXIgYmFzZWQgb24gbnVtYmVyIG9mIApwYXNzLWZpbHRlciBjZWxscyAoQSBoaWdoZXIgZmlsdGVyUmF0aW8gbWVhbnMgdGhhdCBtb3JlIGNlbGxzIGFyZSByZW1vdmVkKQplLmcuIDUwMDAgY2VsbHMKCm1heGltdW0gd291bGQgYmUgZmlsdGVyUmF0aW8gKiA1MDAwIC8gMTAwMDAwID0gZmlsdGVyUmF0aW8gKiA1MDAwICogMC4wNQoKKipUaGlzIHdheSBzYW1wbGVzIHdpdGggZGlmZmVyZW50IHBlcmNlbnRhZ2Ugb2YgZG91YmxldHMgd2lsbCBiZSBmaWx0ZXJlZCBhY2NvcmRpbmdseS4qKgoKYGBge3J9CiMgaW4gb3VyIGNhc2Ugd2Ugbm93IGhhdmUgMTAgMjUxIGNlbGxzIGFzIG9wcG9zZWQgdG8gMTAgNjYxIGNlbGxzIGJlZm9yZQojIGZpbHRlcmluZyAtPiA0MTAgY2VsbHMgd2VyZSByZW1vdmVkICgzLjg1JSkKcHJvaiA8LSBmaWx0ZXJEb3VibGV0cyhBcmNoUlByb2ogPSBwcm9qKQpgYGAKCgojIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiAmIENsdXN0ZXJpbmcKCiogdHdvIG90aGVyIGFsZ29yaXRobXM6CiAgKyBsYXRlbnQgc2VtYW50aWMgaW5kZXhpbmcgKExTSSkgaW4gU2lnbmFjCiAgKyBsYW5kbWFyayBkaWZmdXNpb24gbWFwcyAoTERNKSBpbiBTbmFwQVRBQwogIAoqIEFyY2hSOiBvcHRpbWl6ZWQgaXRlcmF0aXZlIExTSSBtZXRob2QgCiAgKyBleGhpYml0cyBsZXNzIHN1c2NlcHRpYmlsaXR5IHRvIGJhdGNoIGVmZmVjdHMgCiAgKyBmb2N1c2VzIG9uIG1vc3QgdmFyaWFibGUgZmVhdHVyZXMgCiAgMS4gY3JlYXRlIGEgTFNJIFJlZHVjdGlvbiBmcm9tIGEgc3Vic2V0IG9mIHRoZSB0b3RhbCBjZWxscwogIDIuIGxpbmVhcmx5IHByb2plY3QgdGhlIHJlbWFpbmluZyBjZWxscyBpbnRvIHRoaXMgc3Vic3BhY2Ugd2l0aCBMU0kgcHJvamVjdGlvbgogIChiYXNlZCBvbiBTVkQpCgpCZWNhdXNlIHdlIGNhbiBoYXZlIG1heGltYWxseSB0d28gYWNjZXNzaWJsZSBhbGxlbGVzIHBlciBjZWxsLCB0aGUgc2NBVEFDLXNlcSBkYXRhCmlzIHNwYXJzZS4gVGhlcmVmb3JlLCB0aGUgbWFqb3JpdHkgb2YgYWNjZXNzaWJsZSByZWdpb25zIGFyZSBub3QgdHJhbnNwb3NlZCwgbWVhbmluZyAKdGhhdCBtb3N0IGxvY2kgd2lsbCBoYXZlIDAgYWNjZXNzaWJsZSBhbGxlbGVzLiBBIHplcm8gY291bGQgbWVhbiAibm9uLWFjY2Vzc2libGUiIApvciAibm90IHNhbXBsZWQiLiBGb3IgbWFueSBhbmFseXNpcyB3ZSBjYW4gdXNlIGEgYmluYXJpemVkIG1hdGl4LiAqKkltcG9yYW50bHksKioKKip0aGUgMXMgaGF2ZSBpbmZvcm1hdGlvbiwgQlVUIHRoZSAwcyBkbyBub3QhKioKCkEgUENBIHdvdWxkIHJlc3VsdCBpbiBoaWdoIGludGVyLWNlbGwgc2ltaWxhcml0eSBhdCBhbGwgMCBwb3NpdGlvbnMuIEFuIGFsdGVybmF0aXZlCmFwcHJvYWNoIGZvciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gaXMgYSAqKmxheWVyZWQgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uKiouIEZpcnN0LAoqKkxhdGVudCBTZW1hbnRpYyBJbmRleGluZyAoTFNJKSoqIGlzIHVzZWQuIExTSSBpcyBhbiBhcHByb2FjaCBmcm9tIGxhbmd1YWdlCnByb2Nlc3NpbmcuIERpZmZlcmVudCBzYW1wbGVzIGFyZSB0aGUgImRvY3VtZW50cyIgYW5kIGRpZmZlcmVudCByZWdpb25zL3BlYWtzIGFyZQp0aGUgIndvcmRzIi4gCgojIyBJdGVyYXRpdmUgTFNJCgoxLiBjb21wdXRlIHRlcm0gZnJlcXVlbmN5IChkZXB0aCBub3JtYWxpemF0aW9uIHRvIDEwLDAwMCBwZXIgc2luZ2xlIGNlbGwpCgokVEYgPSBcZnJhY3tDX3tpan19e0Zfe2p9fSQgd2l0aCAkQ197aWp9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNvdW50cyBmb3IgcGVhayBpIGluIGNlbGwgaiBhbmQgJEZfe2p9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNvdW50cyBpbiBjZWxsIGouCgoyLiBJbnZlcnNlIGRvY3VtZW50IGZyZXF1ZW5jeSAKICAqIHdlaWdodHMgZmVhdHVyZXMgYnkgaG93IG9mdGVuIHRoZXkgb2NjdXIgCiAgKiBtb3JlIHdlaWdodCB0byBsZXNzIGZyZXF1ZW50IHBlYWtzCgokSURGID0gXGZyYWN7Tn17bl97cH19JCB3aXRoIE4gYmVpbmcgdGhlIHRvdGFsIG51bWJlciBvZiBjZWxscyBpbiB0aGUgZGF0YXNldCBhbmQgJG5fe3B9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNvdXRucyBmb3IgcGVhayBpIGFjcm9zcyBhbGwgY2VsbHMuCgozLiBUaGUgdGVybSBmcmVxdWVuY3kgVEYgaXMgbm9ybWFsaXplZCBieSB0aGUgaW52ZXJzZSBkb2N1bWVudCBmcmVxdW5jeSBJREYuIApZb3UgZ2V0IGEgKipURi1JREYqKiBtYXRyaXggKHRlcm0gZnJlcXVlbmN5LWludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5KSB3aGljaAp0ZWxscyB1cyBob3cgaW1wb3J0YW50IGEgcmVnaW9uL3BlYWsgaXMgdG8gYSBzYW1wbGUuIEluIG90aGVyIHdvcmRzIHlvdSB0cmFuc2Zvcm0KYSBiaW5hcnkgbWF0cml4IHRvIGEgbm9uLWJpbmFyeSBtYXRyaXguCgokVEYtSURGID0gXGxvZ3sxICsgKFRGICogSURGKSAxMF57NH19JAoKNC4gU1ZEIGlkZW50aWZpZXMgdGhlIG1vc3QgdmFsdWFibGUgaW5mb3JtYXRpb24gYWNyb3NzIHNhbXBsZXMuIFRoZW4gCndlIGNhbiB1c2UgdGhlc2UgbW9zdCB2YWx1YWJsZSBmZWF0dXJlcyB0byByZXByZXNlbnQgdGhlIGRhdGEgaW4gYSBsb3dlciBkaW1lbnNpb25hbCBzcGFjZQo1LiBDbHVzdGVycyBhcmUgaWRlbnRpZmllZCB3aXRoIFNldXJhdCdzIFNoYXJlZCBOZWFyZXN0IE5laWdoYm9yIGNsdXN0ZXJpbmcKNi4gU3VtIGFjY2Vzc2liaWxpdHkgYWNyb3NzIGFsbCBzaW5nbGUgY2VsbHMgaW4gZWFjaCBjbHVzdGVyIC0+IGxvZy1ub3JtYWxpemUKNy4gSWRlbnRpZnkgbW9zdCB2YXJpYWJsZSBmZWF0dXJlcyBhY3Jvc3MgdGhlIGNsdXN0ZXJzCjguIHJlcGVhdCB3aXRoIG1vc3QgdmFyaWFibGUgcGVha3MgYXMgZmVhdHVyZXMKCldpdGggTFNJIHdlIGNhbiByZWR1Y2UgdGhlIGRpbWVuc2lvbmFsaXR5IG9mIHRoZSBzcGFyc2UgaW5zZXJ0aW9uIG1hdHJpeCB0byB0ZW5zIApvciBodW5kcmVkcy4gVGhlbiBVTUFQIG9yIHQtU05FIGNhbiBiZSB1c2VkIHRvIHZpc3VhbGl6ZSB0aGUgZGF0YQoKClVubGlrZSBpbiBzY1JOQS1zZXEgd2UgY2Fubm90IHNlbGVjdCB0aGUgdG9wIGhpZ2hseSB2YXJpYWJsZSBmZWF0dXJlcyBiZWZvcmUgCmRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiAoaGlnaCBub2lzZSwgbG93IHJlcHJvZHVjaWJpbGl0eSkuIFJhdGhlciB0aGUgaXRlcmF0aXZlIApMU0kgYXBwcm9hY2ggZmlyc3QgY29tcHV0ZXMgYSBMU0kgCm9uIHRoZSBtb3N0IGFjY2Vzc2libGUgdGlsZXMgKHRoaXMgd2lsbCBpZGVudGlmeSBjbHVzdGVycyBjb3JyZXNwb25kaW5nIHRvIHRoZSAKbWFqb3IgY2VsbCB0eXBlcykuIFRoZW4sIEFyY2hSIGNvbXB1dGVzIHRoZSBhdmVyYWdlIGFjY2Vzc2liaWxpdHkgYWNyb3NzIHRoZXNlIApjbHVzdGVycyBhY3Jvc3MgYWxsIGZlYXR1cmVzLiBOZXh0LCB0aGUgbW9zdCB2YXJpYWJsZSBwZWFrcyBhY3Jvc3MgdGhlc2UgY2x1c3RlcnMKYXJlIGlkZW50aWZpZWQuIFRoZSBtb3N0IGhpZ2hseSBhY2Nlc3NpYmxlIHBlYWtzIGFyZSB0aGUgZmVhdHVyZXMgb2YgYSBuZXcgCnJvdW5kIG9mIExTSS4gV2UgY2FuIHNldCBob3cgbWFueSByb3VuZHMgb2YgTFNJIHdlIHdhbnQgdG8gYmUgcGVmb3JtZWQuIAoKVXNpbmcgaXRlcmF0aXZlIExTSSByZWR1Y2VzIGJhdGNoIGVmZmVjdHMuIElmIHlvdSBzZWUgc29tZSBiYXRjaCBlZmZlY3RzIHlvdSBjb3VsZAp0cnkgdG8gYWRkIG1vcmUgTFNJIGl0ZXJhdGlvbnMgYW5kIHN0YXJ0IGZyb20gYSBsb3dlciBpbml0aWFsIGNsdXN0ZXJpbmcgCnJlc29sdXRpb24uIEFsc28sIHRoZSBudW1iZXIgb2YgdmFyaWFibGUgZmVhdHVyZXMgY2FuIGJlIGxvd2VyZWQuICAjCgoKYGBge3J9CnByb2ogPC0gYWRkSXRlcmF0aXZlTFNJKEFyY2hSUHJvaiA9IHByb2osIHVzZU1hdHJpeCA9ICJUaWxlTWF0cml4IiwgbmFtZSA9ICJJdGVyYXRpdmVMU0kiKQoKYGBgCgoKIyMgQ2x1c3RlcmluZwoKQ2FsbGluZyBjbHVzdGVycyBpbiB0aGlzIG5ldyBzcGFjZSB1c2VzIHRoZSBTZXVyYXQncyBncmFwaCBjbHVzdGVyaW5nIGZ1bmN0aW9uCmFzIGRlZmF1bHQgY2x1c3RlcmluZyBtZXRob2QuIFRoZSBTZXVyYXQgCm1ldGhvZCBmaXJzdCBjb21wdXRzIEtOTiBncmFwaCBhbmQgdGhlbiBhICBtb2R1bGFyaXR5IG9wdGltaXphdGlvbgp0ZWNobmlxdWUgdG8gY2x1c3RlciB0aGUgY2VsbHMgKGl0ZXJhdGl2ZWx5IGdyb3VwIGNlbGxzIHRvZ2V0aGVyCndpdGggTG91dmlhbiBhbGdvcml0aG0gdXNpbmcgMTAgcmFuZG9tIHN0YXJ0cykuIEFub3RoZXIgb3B0aW9uIAppcyB0byB1c2UgIlNjcmFuIi4gVGhlCmRlZmF1bHQgbnVtYmVyIG9mIG5lYXJlc3QgbmVpZ2hib3JzIHVzZWQKaXMgMTAuIFRoZSBtaW5pbXVtIG51bWJlciBvZiBjZWxscyBmb3IgYSBjbHVzdGVyIHRvIGJlIGNhbGxlZCBhIApjbHVzdGVyIGlzIHNldCB0byA1IGJ5IGRlZmF1bHQuIFRoZSBtYXhpbXVtIG51bWJlciBvZiBjbHVzdGVycyB0byAKYmUgY2FsbGVkIGlzIHNldCB0byAyNSBieSBkZWZhdWx0LiAKCmBgYHtyfQpwcm9qIDwtIGFkZENsdXN0ZXJzKGlucHV0ID0gcHJvaiwgcmVkdWNlZERpbXMgPSAiSXRlcmF0aXZlTFNJIikKYGBgCgoKIyBWaXN1YWxpemluZyBhIFVNQVAgZW1iZWRkaW5nCgoqIHVzZXMgdXdvdCBwYWNrYWdlCiogdmFyaW91cyBhdHRyaWJ1dGVzIG9mIHRoZSBkYXRhIGNhbiBiZSB2aXN1YWxpemVkCiAgKyB0aGVzZSBhcmUgc3RvcmVkIGluIGEgbWF0cml4IGNhbGxlZCBgY2VsbENvbERhdGFgCiAgKyB3aGljaCB2YXJpYWJsZSB0aGUgcGxvdCBpcyBjb2xvcmVkIGJ5IGlzIHNwZWNpZmllZCBieSBgY29sb3JCeWAKICBhbmQgYG5hbWVgIHBhcmFtZXRlcgoKYGBge3J9CnByb2ogPC0gYWRkVU1BUChBcmNoUlByb2ogPSBwcm9qLCByZWR1Y2VkRGltcyA9ICJJdGVyYXRpdmVMU0kiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTB9CmRmIDwtIGFzX2RhdGFfZnJhbWUoY2JpbmQoZ2V0Q2VsbENvbERhdGEocHJvaiksIGdldEVtYmVkZGluZyhwcm9qKSkgKSAlPiUKICByZW5hbWUoYyh1bWFwMSA9IEl0ZXJhdGl2ZUxTSS5VTUFQX0RpbWVuc2lvbl8xLCB1bWFwMiAgPSBJdGVyYXRpdmVMU0kuVU1BUF9EaW1lbnNpb25fMikpCgp2YXJpYWJsZXMgPC0gYygiQ2x1c3RlcnMiLCAiU2FtcGxlIiwgIm5GcmFncyIsICJEb3VibGV0U2NvcmUiKQoKcGxvdHMxIDwtIG1hcChjKCJDbHVzdGVycyIsICJTYW1wbGUiKSwgZnVuY3Rpb24obil7CiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkZiAlPiUgcHVsbCgidW1hcDEiKSwgeSA9IGRmICU+JSBwdWxsKCJ1bWFwMiIpLCAKICAgICAgICAgICAgICAgICBjb2wgPSBkZiAlPiUgcHVsbChuKSksIHNpemUgPSAuMDQpICsKICAgIGd1aWRlcyhjb2w9Z3VpZGVfbGVnZW5kKHRpdGxlPXBhc3RlMChuKSkpICsKICAgIHhsYWIoInVtYXAxIikgKwogICAgeWxhYigidW1wYTIiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKG4pKQp9KQoKcGxvdHMyIDwtIG1hcChjKCJuRnJhZ3MiLCAiRG91YmxldFNjb3JlIiksIGZ1bmN0aW9uKG4pewogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gZGYgJT4lIHB1bGwoInVtYXAxIiksIHkgPSBkZiAlPiUgcHVsbCgidW1hcDIiKSwgCiAgICAgICAgICAgICAgICAgY29sID0gZGYgJT4lIHB1bGwobikpLCBzaXplID0gLjA0KSArCiAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGU9cGFzdGUwKG4pKSkgKwogICAgeGxhYigidW1hcDEiKSArCiAgICB5bGFiKCJ1bXBhMiIpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZTAobikpCn0pCgpkby5jYWxsKGdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlLCBjKHBsb3RzMSwgbmNvbD0yKSkjLCBucm93ID0gMikpCgpgYGAKCgojIENsdXN0ZXIgYXNzaWdubWVudCB1c2luZyBnZW5lIHNjb3JlcwoKRm9yIHRoZSB0b3kgZGF0YXNldCBtYXJrZXIgZ2VuZXMgb2Yga25vd24gaGVtYXRvcG9pZXRpYyByZWd1bGF0b3JzCmNhbiBiZSB1c2VkLiBVc2luZyBNQUdJQyB3ZSBhZGQgaW1wdXRhdGlvbiB3ZWlnaHRzIHRvIHNtb290aCB0aGUgZHJvcG91dCBub2lzZSBpbiB0aGUgZ2VuZSBzY29yZXMKCmBgYHtyfQpwcm9qIDwtIGFkZEltcHV0ZVdlaWdodHMocHJvaikKYGBgCgpgYGB7cn0KbWFya2VyR2VuZXMgIDwtIGMoCiAgICAiQ0QzNCIsICAjRWFybHkgUHJvZ2VuaXRvcgogICAgIkdBVEExIiwgI0VyeXRocm9pZAogICAgIlBBWDUiLCAiTVM0QTEiLCAiTU1FIiwgI0ItQ2VsbCBUcmFqZWN0b3J5CiAgICAiQ0QxNCIsICJNUE8iLCAjTW9ub2N5dGVzCiAgICAiQ0QzRCIsICJDRDhBIiNUQ2VsbHMKICApCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KcCA8LSBwbG90RW1iZWRkaW5nKAogICAgQXJjaFJQcm9qID0gcHJvaiwgCiAgICBjb2xvckJ5ID0gIkdlbmVTY29yZU1hdHJpeCIsIAogICAgbmFtZSA9IG1hcmtlckdlbmVzLCAKICAgIGVtYmVkZGluZyA9ICJVTUFQIiwKICAgIGltcHV0ZVdlaWdodHMgPSBnZXRJbXB1dGVXZWlnaHRzKHByb2opCikKZG8uY2FsbChncmlkRXh0cmE6OmdyaWQuYXJyYW5nZSwgYyhwLCBuY29sID0gMykpCmBgYAoKCiMgVmlzdWFsaXppbmcgR2Vub21lIEJyb3dzZXIgVHJhY2tzCgpCcm93c2UgbG9jYWwgY2hyb21hdGluIGFjY2Vzc2liaWxpdHkgYXQgbWFya2VyIGdlbmVzLiBQbG90IGdlbm9tZQpicm93c2VyIHRyYWNrcyBwZXIgY2x1c3RlcgpgYGB7cn0KcCA8LSBwbG90QnJvd3NlclRyYWNrKAogICAgQXJjaFJQcm9qID0gcHJvaiwgCiAgICBncm91cEJ5ID0gIkNsdXN0ZXJzIiwgCiAgICBnZW5lU3ltYm9sID0gbWFya2VyR2VuZXMsIAogICAgdXBzdHJlYW0gPSA1MDAwMCwKICAgIGRvd25zdHJlYW0gPSA1MDAwMAopCmBgYAoKCmBgYHtyfQpncmlkOjpncmlkLm5ld3BhZ2UoKQpncmlkOjpncmlkLmRyYXcocCRDRDE0KQpgYGAKCgoKIyBJbnRlZ3JhdGlvbiB3aXRoIHNjUk5BLXNlcQoKKiB0aGUgc2NBVEFDLXNlcSBnZW5lIHNjb3JlIG1hdHJpeCBpcyBjb21wYXJlZCB3aXRoIHRoZSBzY1JOQS1zZXEgZ2VuZSBleHByZXNzaW9uCm1hdHJpeAoqIGZvciB0aGlzIGFsaWdubWVudCB0aGUgYEZpbmRUcmFuc2ZlckFuY2hvcnMoKWAgZnVuY3Rpb24gZnJvbSBTZXVyYXQgaXMgdXNlZAoqIHRvIHNjYWxlIGZvciBsYXJnZSBzYW1wbGUgc2l6ZSwgdGhpcyBwcm9jZXNzIGlzIHBhcmFsbGVsaXplZAoqIGZvciBlYWNoIGNlbGwgaW4gQVRBQyB3ZSBmaW5kIHRoZSBjZWxsIGluIHNjUk5BLXNlcSB0aGF0IGxvb2tzIG1vc3Qgc2ltaWxhciAKLT4gYXNzaWduIHRoZSBjb3JyZXBzb25kaW5nIGdlbmUgZXhwcmVzc2lvbiB0byB0aGF0IGNlbGwuIAoKQXBhcnQgZnJvbSB1c2luZyB0aGlzIGluZm9ybWF0aW9uIGZvciBpZGVudGlmeWluZyBjbHVzdGVycyB3ZSBjYW4gYWxzbyB1c2UgaXQgCmZvciBpZGVudGlmeWluZyBwcmVkaWN0ZWQgY2lzLXJlZ3VsYXRvcnkgZWxlbWVudHMuCgoKYGBge3J9CmlmKCFmaWxlLmV4aXN0cygic2NSTkEtSGVtYXRvcG9pZXNpcy1HcmFuamEtMjAxOS5yZHMiKSl7CiAgICBkb3dubG9hZC5maWxlKAogICAgICAgIHVybCA9ICJodHRwczovL2plZmZncmFuamEuczMuYW1hem9uYXdzLmNvbS9BcmNoUi9UZXN0RGF0YS9zY1JOQS1IZW1hdG9wb2llc2lzLUdyYW5qYS0yMDE5LnJkcyIsCiAgICAgICAgZGVzdGZpbGUgPSAic2NSTkEtSGVtYXRvcG9pZXNpcy1HcmFuamEtMjAxOS5yZHMiCiAgICApCn0KCiMgcmFuZ2VkIHN1bW1hcml6ZWQgRXhwZXJpbWVudApzZVJOQSA8LSByZWFkUkRTKCJzY1JOQS1IZW1hdG9wb2llc2lzLUdyYW5qYS0yMDE5LnJkcyIpCnNlUk5BCmBgYAoKTGV0cyBoYXZlIGEgbG9vayBhdCB0aGUgY291bnQgbWF0cml4OgoKYGBge3J9CiMgc3BhcnNlIGNvdW50IG1hdHJpeCBvZiBzY1JOQS1zZXEKYXNzYXlzKHNlUk5BKVtbMV1dWzE6MTAsIDE6NV0KYGBgCgpNZXRhZGF0YSBvZiB0aGUgc2NSTkEtc2VxIGRhdGFzZXQ6CgpXZSBhbHJlYWR5IGhhdmUgY2x1c3RlcmluZywgdW1hcCBlbWJlZGRpbmdzIGFuZCBjZWxsIHR5cGVzLgoKYGBge3IsIHJlc3VsdHMgPSAiYXNpcyJ9CmNvbERhdGEoc2VSTkEpICU+JSBoZWFkICU+JSBrbml0cjo6a2FibGUoKQpgYGAKClBsb3QgUXVhbGl0eSBNZXRyaWNzIG9mIHRoZSBzY1JOQS1zZXEgZGF0YXNldDoKCmBgYHtyLCBmaWcud2lkdGg9MTB9CmFzX2RhdGFfZnJhbWUoY29sRGF0YShzZVJOQSkpICU+JSAKICBzZWxlY3QoblVNSSwgbkdlbmUsIEdyb3VwKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAhR3JvdXAsIG5hbWVzX3RvID0gInN0YXQiKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fdmlvbGluKGFlcyh4ID0gR3JvdXAsIHkgPSB2YWx1ZSwgZmlsbCA9IEdyb3VwKSkgKwogIGZhY2V0X3dyYXAofnN0YXQsIHNjYWxlcyA9ICJmcmVlIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgeGxhYigiU2FtcGxlIikKICAjcGl2b3RfbG9uZ2VyKGNvbHMgPSAhcykKYGBgCgoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTEwfQpkZiA8LSBhc19kYXRhX2ZyYW1lKGNvbERhdGEoc2VSTkEpKQoKcDEgPC0gZ2dwbG90KCkgKwpnZW9tX3BvaW50KGFlcyh4ID0gZGYgJT4lIHB1bGwoIlVNQVAxIiksIHkgPSBkZiAlPiUgcHVsbCgiVU1BUDIiKSwgCiAgICAgICAgICAgICAgIGNvbCA9IGRmICU+JSBwdWxsKCJCaW9DbGFzc2lmaWNhdGlvbiIpKSwgc2l6ZSA9IC4wNCkgKwogIGd1aWRlcyhjb2w9Z3VpZGVfbGVnZW5kKHRpdGxlPSJDZWxsVHlwZSIpKSArCiAgeGxhYigidW1hcDEiKSArCiAgeWxhYigidW1wYTIiKSArCiAgbGFicyh0aXRsZSA9ICJzY1JOQS1zZXEgZGF0YXNldCAtIGNlbGwgdHlwZSIpCgpwMiA8LSBnZ3Bsb3QoKSArCmdlb21fcG9pbnQoYWVzKHggPSBkZiAlPiUgcHVsbCgiVU1BUDEiKSwgeSA9IGRmICU+JSBwdWxsKCJVTUFQMiIpLCAKICAgICAgICAgICAgICAgY29sID0gZGYgJT4lIHB1bGwoIm5HZW5lIikpLCBzaXplID0gLjA0KSArCiAgZ3VpZGVzKGNvbD1ndWlkZV9sZWdlbmQodGl0bGU9Im51bWJlciBvZiBnZW5lcyIpKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIHhsYWIoInVtYXAxIikgKwogIHlsYWIoInVtcGEyIikgKwogIGxhYnModGl0bGUgPSAic2NSTkEtc2VxIGRhdGFzZXQgLSBnZW5lIG51bWJlciIpCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSwgcDIsIG5jb2wgPSAxKQpgYGA=